import time
import logging
import json
from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware
from typing import Dict, Any

from app.core.cons import Cons
from app.utils.request_utils import RequestUtils
from .config import settings

logger = logging.getLogger(__name__)

# 使用属性访问方式获取拦截器配置
interceptor_config = settings.interceptor_config

class ApiRequestLogger(BaseHTTPMiddleware):
    """专门拦截/api/*请求的日志记录中间件，在一条日志中打印请求信息、响应体、请求头，并根据耗时决定日志级别"""
    
    async def dispatch(self, request: Request, call_next) -> Response:
        # 检查是否在白名单中或不是/api/*路径
        if (any(request.url.path.startswith(path) for path in interceptor_config.whitelist) or 
            not request.url.path.startswith('/api/')):
            return await call_next(request)
        
        # 记录请求开始时间
        start_time = time.time()
        
        # 获取基本请求信息
        client_ip = request.client.host if request.client else "unknown"
        method = request.method
        path = request.url.path
        
        # 使用工具方法获取请求参数
        request_params = await RequestUtils.get_request_params(
            request, 
            truncate_length=interceptor_config.response.truncate_length
        )
        
        # 处理敏感头信息
        sanitized_headers = RequestUtils.sanitize_headers(dict(request.headers))
        
        # 重新创建请求对象以确保后续处理能正常获取（如果读取了请求体）
        if 'raw_body' in request_params:
            request = Request(
                scope=request.scope,
                receive=lambda: {'type': 'http.request', 'body': request_params['raw_body']}
            )
        
        try:
            # 调用下一个处理程序
            response = await call_next(request)
            
            # 计算耗时
            process_time = time.time() - start_time
            
            # 获取响应信息
            response_data = await self._process_response(response)
            
            # 根据耗时决定日志级别
            log_func = self._get_log_function(process_time)
            
            # 获取查询参数和请求体
            query_params = request_params.get('query_params', {})
            request_body = request_params.get('body', None)
            
            # 构建日志消息
            log_message = (
                f"API Request: {method} {path} from {client_ip}, "
                f"Query Params: {query_params}, "
                f"Request Body: {request_body}, "
                f"Response: Status={response.status_code}, "
                f"Time={process_time:.3f}s, "
                f"Response Body: {response_data['content']}, "
                # f"Request Headers: {sanitized_headers}"
            )
            
            log_func(log_message)
            # 返回新创建的响应对象，其中包含了完整的响应体
            return response_data['response']
            
        except Exception as e:
            # 记录异常
            process_time = time.time() - start_time
            logger.error(
                f"API Error: {method} {path} from {client_ip} - Time: {process_time:.3f}s - Error: {str(e)}"
            )
            raise
    
    def _get_log_function(self, process_time: float):
        """根据处理时间获取适当的日志函数"""
        if process_time > interceptor_config.slow_request.threshold:
            return getattr(logger, interceptor_config.slow_request.log_level.lower())
        return logger.info
    
    async def _process_response(self, response: Response) -> Dict[str, Any]:
        """处理响应数据"""
        # 获取响应体
        response_body = b""
        async for chunk in response.body_iterator:
            response_body += chunk
        
        # 处理响应内容
        response_content = ""
        try:
            # 尝试解析为JSON
            response_json = json.loads(response_body.decode(Cons.UTF_8, errors='ignore'))
            response_content = json.dumps(response_json, ensure_ascii=False, indent=None)
        except:
            # 如果不是JSON，则使用原始内容
            response_content = response_body.decode(Cons.UTF_8, errors='ignore')
        
        # 截断过长的响应内容
        truncate_length = interceptor_config.response.truncate_length
        if len(response_content) > truncate_length:
            response_content = response_content[:truncate_length] + "... [truncated]"
        
        # 创建一个新的Response对象，确保响应体被正确设置
        new_response = Response(
            content=response_body,
            status_code=response.status_code,
            headers=dict(response.headers),
            media_type=response.media_type
        )
        
        return {
            'content': response_content,
            'body': response_body,
            'response': new_response
        }
        